home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d12 / csrc1.arc / CREF.C < prev    next >
C/C++ Source or Header  |  1989-07-27  |  13KB  |  546 lines

  1. /*
  2.  *                C R E F . C
  3.  *
  4.  * This program was distributed to the Unix UUCP network by "cca!z" on
  5.  * Wed Jun 23 14:19:25 1982.  It has not been converted for Decus C
  6.  */
  7.  
  8. /* cref - cross reference program */
  9.  
  10. #include <sys/types.h>
  11. #include <sys/stat.h>
  12. #include <ctype.h>
  13. #include <stdio.h>
  14. #include <vadvise.h>
  15.  
  16. #define    HRATIO    30
  17. #define    LBUFSIZ    2048
  18. #define    LREFS    15
  19. #define    NKEYS    (sizeof(keytab)/sizeof(struct key))
  20. #define    RSEGSIZ    20
  21. #define    SYMSIZ    64
  22. #define    Perror()    perror("cref"),exit(1)
  23. #define    issym(c)    (isalnum(c) || c=='_' || c=='#')
  24.  
  25. char    tflag;                /* Produce tags style output */
  26. char    *lastext;
  27. char    *calloc();
  28. char    prcom[80] = "pr -h ";
  29. char    linebuf[LBUFSIZ], *nextsym(), *putline(), *strcpy();
  30. int    file1 = 1, hconst = 1, lastline, line, nsyms;
  31. short    braces, bracks, parens;
  32. FILE    *fopen(), *fp, *popen(), *pfp;
  33.  
  34. struct    {
  35.     unsigned blank1      : 1;        /* First column is a blank */
  36.     unsigned casef      : 1;        /* Case keyword */
  37.     unsigned comp      : 1;        /* Compound statement */
  38.     unsigned define      : 1;        /* "Define" type statement */
  39.     unsigned indent      : 1;        /* Statement indented */
  40.     unsigned qmark      : 1;        /* Question mark encountered */
  41.     unsigned semic      : 1;        /* Semicolon encountered */
  42.     unsigned preproc  : 1;        /* Preprocessor keyword */
  43. } flags;
  44.  
  45. struct    key    {
  46.     char    *keyword;
  47.     unsigned cflag    : 1;        /* Compound statement flag */
  48.     unsigned dflag    : 1;        /* Defining flag */
  49. } keytab[] =    {
  50.     "#define",    0,1,
  51.     "#else",    0,0,
  52.     "#endif",    0,0,
  53.     "#if",        0,0,
  54.     "#ifdef",    0,0,
  55.     "#ifndef",    0,0,
  56.     "#include",    0,0,
  57.     "#line",    0,1,
  58.     "#undef",    0,0,
  59.     "asm",        0,0,
  60.     "auto",        0,1,
  61.     "break",    0,0,
  62.     "case",        1,0,
  63.     "char",        0,1,
  64.     "continue",    0,0,
  65.     "default",    0,0,
  66.     "do",        1,0,
  67.     "double",    0,1,
  68.     "else",        1,0,
  69.     "entry",    0,0,
  70.     "extern",    0,1,
  71.     "float",    0,1,
  72.     "for",        1,0,
  73.     "goto",        0,0,
  74.     "if",        1,0,
  75.     "int",        0,1,
  76.     "long",        0,1,
  77.     "register",    0,1,
  78.     "return",    0,0,
  79.     "short",    0,1,
  80.     "sizeof",    0,0,
  81.     "static",    0,1,
  82.     "struct",    0,1,
  83.     "switch",    1,0,
  84.     "typedef",    0,1,
  85.     "union",    0,1,
  86.     "unsigned",    0,1,
  87.     "void",        0,1,
  88.     "while",    1,0
  89. };
  90.  
  91. struct    refseg    {
  92.     struct refseg    *nseg;        /* Pointer to next segment */
  93.     struct    {
  94.         int    ref;        /* Line number of reference */
  95.         char    *text;        /* Text of line containing reference */
  96.         short    file;        /* File in which reference occurred */
  97.         char    deflag;        /* Define flag */
  98.     } refs[RSEGSIZ];
  99. } *pseg;
  100.  
  101. struct    symbol    {
  102.     char    name[SYMSIZ+1];
  103.     int    nref;
  104.     struct    refseg    *firstseg;
  105.     struct    refseg    *curseg;
  106. } *htab;
  107.  
  108. main(argc, argv)
  109. int    argc;
  110. char    *argv[];
  111. {
  112.     char    *p, symbol[SYMSIZ+1];
  113.     int    hcode, i, j, k, nslots, t, tchars;
  114.     short    nfile, nkey, rnum;
  115.     struct    stat statb;
  116.  
  117.     tchars = 0;
  118.     if (argc < 2)
  119.         exit(1);        /* No filenames */
  120.     while (*argv[file1] == '-') {
  121.         if (argv[file1][1] == 't')
  122.             tflag++;
  123.         else if (!(hconst = atoi(&argv[file1][1]))) {
  124.             fprintf(stderr, "cref: Bad option");
  125.             exit(1);
  126.         }
  127.         file1++;
  128.     }
  129.     for (nfile = file1; nfile < argc; nfile++) {
  130.         if (stat(argv[nfile], &statb))
  131.             Perror();    /* File doesn't exist */
  132.         tchars += statb.st_size;
  133.     }
  134.     vadvise(VA_ANOM);
  135.     if (!(htab = (struct symbol *)calloc(nslots = hconst*tchars/HRATIO, sizeof(struct symbol)))) {
  136.         fprintf(stderr, "cref: Not enough memory.\n");
  137.         exit(1);
  138.     }
  139.  
  140. /* Main loop for crunching down files */
  141.  
  142.     for (nfile = file1; nfile < argc; nfile++) {
  143.         if (!(fp = fopen(argv[nfile], "r")))    /* Open input file */
  144.             fprintf(stderr, "cref: Can't open %s\n", argv[nfile]);
  145.         strcpy(&prcom[6], argv[nfile]);    /* Put name in header */
  146.         if (!tflag)
  147.             pfp = popen(prcom, "w"); /* Pipe output through "pr" */
  148.         while (fgets(linebuf, LBUFSIZ, fp)) {    /* Statement loop */
  149.             line++;
  150.             if (!tflag) {
  151.                 fprintf(pfp, "%6d  ", line);
  152.                 fputs(linebuf, pfp);
  153.             }
  154.             flags.blank1 = flags.define = flags.casef = flags.comp = flags.indent
  155.                 = flags.semic = flags.qmark = flags.preproc = 0;
  156.             p = linebuf;
  157.             flags.blank1 = *p==' ' || *p=='\t';
  158.             while (p = nextsym(p)) {
  159.                 for (i = 0; i < SYMSIZ && issym(*p); i++)
  160.                     symbol[i] = *p++;
  161.                 symbol[i] = '\0';
  162.                 if ((nkey = binary(symbol, keytab, NKEYS)) >= 0) {
  163.                     if (!parens)    /* So casts don't count as definitions */
  164.                         flags.define |= keytab[nkey].dflag;
  165.                     if (!strcmp(keytab[nkey].keyword, "#include")) {
  166.                         while (*p != '\n')
  167.                             p++;
  168.                         p++;
  169.                         break;
  170.                     }
  171.                     if (!strcmp(keytab[nkey].keyword, "case"))
  172.                         flags.casef = 1;
  173.                 } else {
  174.                     if (!(braces|flags.blank1))
  175.                         flags.define = 1;
  176.                     flags.define |= !flags.indent;
  177.                     if (flags.define)
  178.                         parens = 0;
  179.                     for (j = 0, hcode = 1; j < strlen(symbol); hcode *= symbol[j++]);
  180.                 scan:    for (hcode = abs(hcode) % nslots; hcode >= 0 && *htab[hcode].name &&
  181.                         strcmp(htab[hcode].name, symbol); hcode--);
  182.                     if (hcode < 0) {
  183.                         hcode = nslots -1;
  184.                         goto scan;
  185.                     }
  186.                     if (!*htab[hcode].name) {
  187.                         strcpy(htab[hcode].name, symbol);
  188.                         if (!(htab[hcode].firstseg = htab[hcode].curseg =
  189.                             (struct refseg *)calloc(1, sizeof(struct refseg)))) {
  190.                             fprintf(stderr, "cref: Out of memory!\n");
  191.                             exit(1);
  192.                         }
  193.                         htab[hcode].curseg->refs[0].ref = line;
  194.                         if (!bracks && !parens || !flags.indent)
  195.                             if (htab[hcode].curseg->refs[0].deflag = flags.define |
  196.                                 (*p == ':' && !flags.qmark && !flags.casef))
  197.                                 ctags(hcode, 0, nfile);
  198.                         htab[hcode].nref++;
  199.                         nsyms++;
  200.                     } else {
  201.                         if (!(htab[hcode].nref % RSEGSIZ)) {
  202.                             if (!(htab[hcode].curseg->nseg = (struct refseg *)
  203.                                 calloc(1, sizeof(struct refseg)))) {
  204.                                 fprintf(stderr, "cref: Out of memory!\n");
  205.                                 exit(1);
  206.                             }
  207.                             htab[hcode].curseg = htab[hcode].curseg->nseg;
  208.                         }
  209.                         htab[hcode].curseg->refs[rnum = htab[hcode].nref++ % RSEGSIZ].ref = line;
  210.                         if (!bracks && !parens || !flags.indent)
  211.                             if (htab[hcode].curseg->refs[rnum].deflag = flags.define |
  212.                                 (*p == ':' && !flags.qmark && !flags.casef))
  213.                                 ctags(hcode, rnum, nfile);
  214.                     }
  215.                     if (nsyms == nslots) {
  216.                         fprintf(stderr, "cref: Hash table overflowed!\n");
  217.                         exit(1);
  218.                     }
  219.                     if (*p == ':')    /* End of label */
  220.                         flags.define = 0;
  221.                 }
  222.                 flags.indent = 1;
  223.             }
  224.         }
  225.         fclose(fp);
  226.         if (!tflag)
  227.             pclose(pfp);
  228.     }
  229.  
  230. /* Now print the cref table */
  231.  
  232.     if (tflag) {
  233.         pfp = stdout;
  234.         sort(htab, nslots);
  235.         for (i = nslots - nsyms; i < nslots; i++) {
  236.             pseg = htab[i].firstseg;
  237.             for (j = 0, k = 0, t = 0; j < htab[i].nref; j++) {
  238.                 if (pseg->refs[k].ref != t && pseg->refs[k].deflag) {
  239.                     t = pseg->refs[k].ref;
  240.                     fprintf(pfp, "%s    %s    ?^", htab[i].name, argv[pseg->refs[k].file]);
  241.                     fputs(pseg->refs[k].text, pfp);
  242.                     fprintf(pfp, "$?\n");
  243.                 }
  244.                 if (++k == RSEGSIZ) {
  245.                     pseg = pseg->nseg;    /* Print next segment */
  246.                     k = 0;
  247.                 }
  248.             }
  249.         }
  250.         exit(0);
  251.     }
  252.     pfp = popen("pr -h 'Cref listing'", "w");
  253.     sort(htab, nslots);
  254.     for (i = nslots - nsyms; i < nslots; i++) {
  255.         char lrefs;
  256.  
  257.         lrefs = LREFS;        /* References per line */
  258.         fprintf(pfp, "%s",     mKuname);
  259.         if ((t = strlen(htab[i].name)) > 12) {
  260.             lrefs = LREFS - (t-5)/8;
  261.             for (j = 0; j < 7 - (t-5)%8; j++)
  262.                 putc(' ', pfp);    /* Space after symbol */
  263.         } else
  264.             for (j = 0; j < 12-t; j++)
  265.                 putc(' ', pfp);    /* Space after symbol */
  266.         pseg = htab[i].firstseg;
  267.         for (j=0, k=0, t=0; j < htab[i].nref; j++) {
  268.             if (pseg->refs[k].ref != t) {
  269.                 if (!lrefs--) {
  270.                     fprintf(pfp, "\n        ");
  271.                     lrefs = LREFS;
  272.                 }
  273.                 fprintf(pfp, "%7d", t = pseg->refs[k].ref);
  274.                 if (pseg->refs[k].deflag)
  275.                     putc('#', pfp);
  276.                 else
  277.                     putc(' ', pfp);
  278.             }
  279.             if (++k == RSEGSIZ) {
  280.                 pseg = pseg->nseg;    /* Print next segment */
  281.                 k = 0;
  282.             }
  283.         }
  284.         putc('\n', pfp);
  285.     }
  286.     fprintf(pfp, "\nSymbols = %d        Hash table size = %d        Density = %f\n",
  287.         nsyms, nslots, (double)nsyms/(double)nslots);
  288.     pclose(pfp);
  289. }
  290.  
  291.  
  292.  
  293. /* Create entry for tags file */
  294.  
  295. ctags(hcode, rnum, nfile)
  296. register hcode, rnum, nfile;
  297. {
  298.     register len;
  299.  
  300.     if (!tflag)
  301.         return;
  302.     len = strlen(linebuf);
  303.     if (lastline != line) {
  304.         if (!(lastext = calloc(len+1, 1))) {
  305.             fprintf(stderr, "cref: Out of memory!\n");
  306.             exit(1);
  307.         }
  308.         (void) strcpy(lastext, linebuf);
  309.         lastext[--len] = '\0';
  310.         lastline = line;
  311.     }
  312.     htab[hcode].curseg->refs[rnum].text = lastext;
  313.     htab[hcode].curseg->refs[rnum].file = nfile;
  314. }
  315.  
  316.  
  317.  
  318. /* Binary search for word in tab */
  319.  
  320. binary(word, tab, n)
  321. char    *word;
  322. struct    key    tab[];
  323. int    n;
  324. {
  325.     int    low, high, mid, cond;
  326.  
  327.     low = 0;
  328.     high = n - 1;
  329.     while (low <= high) {
  330.         mid = (low+high)/2;
  331.         if ((cond = strcmp(word, tab[mid].keyword)) < 0)
  332.             high = mid - 1;
  333.         else if (cond > 0)
  334.             low = mid + 1;
  335.         else
  336.             return(mid);
  337.     }
  338.     return(-1);
  339. }
  340.  
  341.  
  342.  
  343. /* Find next symbol in statement, and return a pointer to it.  If end of
  344.  * statement is reached, return null pointer.
  345.  */
  346.  
  347. char    *
  348. nextsym(p)
  349. char    *p;
  350. {
  351.     static symline;
  352.  
  353.     for (; !issym(*p) || isdigit(*p); p++) {
  354.         switch (*p) {
  355.         case '{':
  356.             braces++;
  357.             break;
  358.         case '}':
  359.             braces--;
  360.             break;
  361.         case '\n':
  362.             if (!flags.semic && !flags.comp && symline == line && !flags.preproc ||
  363.                 flags.preproc && *(p-1) == '\\') {
  364.                 if (!fgets(linebuf, LBUFSIZ, fp))
  365.                     return(0);
  366.                 p = linebuf - 1;
  367.                 flags.blank1 = *(p+1)==' ' || *(p+1)=='\t';
  368.                 line++;
  369.                 if (!tflag) {
  370.                     fprintf(pfp, "%6d  ", line);
  371.                     fputs(linebuf, pfp);
  372.                 }
  373.                 break;
  374.             } else
  375.                 return(0);
  376.         case '?':
  377.             flags.qmark = 1;
  378.             break;
  379.         case ':':
  380.             flags.comp |= !flags.qmark;
  381.             break;
  382.         case ';':
  383.             flags.semic = 1;
  384.             break;
  385.         case '\'':
  386.             while (*++p != '\'' || *(p-1) == '\\' && *(p-2) != '\\');
  387.             break;
  388.         case '"':
  389.             while (*++p != '"' || *(p-1) == '\\')
  390.                 if (!(p = putline(p)))
  391.                     return(0);
  392.             break;
  393.         case '/':
  394.             if (*(p+1) != '*')
  395.                 break;
  396.             p++;
  397.             while (*++p != '*' || *(p+1) != '/')
  398.                 if (!(p = putline(p)))
  399.                     return(0);
  400.             p++;
  401.             break;
  402.         case '(':
  403.             parens++;
  404.             flags.indent = 1;
  405.             break;
  406.         case ')':
  407.             parens--;
  408.             break;
  409.         case '[':
  410.             bracks++;
  411.             break;
  412.         case ']':
  413.             bracks--;
  414.             break;
  415.         case '0':
  416.             while (isalnum(*(p+1)))
  417.                 p++;    /* Ignore hex numbers */
  418.             break;
  419.         case ' ':
  420.         case '\t':
  421.             flags.indent = 1;
  422.             break;
  423.         default:
  424.             ;
  425.         }
  426.     }
  427.     if (*p == '#')
  428.         flags.preproc = 1;
  429.     symline = line;        /* Indicate symbol found on this line */
  430.     return(p);
  431. }
  432.  
  433. char    *
  434. putline(p)
  435. char    *p;
  436. {
  437.     if (*p == '\n') {
  438.         if (!fgets(linebuf, LBUFSIZ, fp))
  439.             return(0);
  440.         p = linebuf - 1;
  441.         line++;
  442.         if (!tflag) {
  443.             fprintf(pfp, "%6d  ", line);
  444.             fputs(linebuf, pfp);
  445.         }
  446.     }
  447.     if (*p == '\\' && *(p-1) == '\\')
  448.         *p = '\0';        /* Help out parsing quoted strings */
  449.     return(p);
  450. }
  451.  
  452. sort(tab, n)
  453. struct    symbol    *tab;
  454. int    n;
  455. {
  456.     int    gap, i, j;
  457.     struct    symbol    temp;
  458.  
  459.     for (gap = n/2; gap > 0; gap /= 2)
  460.         for (i = gap; i < n; i++)
  461.             for (j = i-gap; j >= 0; j -= gap) {
  462.                 if (strcmp(tab[j].name, tab[j+gap].name) <= 0)
  463.                     break;
  464.                 temp = tab[j];
  465.                 tab[j] = tab[j+gap];
  466.                 tab[j+gap] = temp;
  467.             }
  468. }
  469.  
  470.  
  471.  
  472. .TH CREF 1 10/30/80
  473. .CC
  474. .SH NAME
  475. cref \- cross reference program
  476. .SH SYNOPSIS
  477. .B cref
  478. [
  479. .B -
  480. .I n
  481. ]
  482. [
  483. .B -t
  484. ]
  485. file...
  486. .SH DESCRIPTION
  487. .I Cref
  488. generates a complete cross reference listing of one or more C programs, printing
  489. the result on the standard output.  A listing of the programs with line numbers
  490. is printed first, followed by the actual cross reference listing.  This latter
  491. contains all the programs's symbols alphabetically arranged, one to a line,
  492. with each line containing the numbers of the lines in the programs where the
  493. symbol was referenced.  If the symbol was defined on a given line, that line
  494. number will be followed by a `#'.  Symbols with more than approximately 15
  495. references occupy multiple lines.  There is no limit on the number of symbols
  496. that
  497. .I cref
  498. will handle, nor on the number of references per symbol.
  499. .PP
  500. .I Cref
  501. stores its symbols in a hash table whose size is determined by
  502. .I cref
  503. based on the total number of characters in the files to be processed.  For
  504. almost all programs, this turns out to be an excellent approximation.
  505. However, for a few programs, generally short header files, there may be
  506. too many symbols for the hash table, and the diagnostic "Hash table
  507. overflowed!" will be printed out.  Since the output of
  508. .I cref
  509. is piped through
  510. .I pr,
  511. it is not really possible for cref to recover from this condition.  Instead,
  512. .I cref
  513. should be rerun with the
  514. .B -n
  515. option, where
  516. .B n
  517. is some number.  This will multiply the starting size of the hash table by
  518. .B n
  519. times.
  520. .PP
  521. If
  522. .I cref
  523. is invoked with the
  524. .B -t
  525. option, instead of its regular output it produces an output identical in
  526. form to that produced by the
  527. .I ctags(1)
  528. program.  The advantage of the
  529. .I cref
  530. output over
  531. .I ctags
  532. is that
  533. .I cref
  534. will flag all variable and macro definitions as well as all function
  535. definitions.
  536. .SH AUTHOR
  537. Steve Zimmerman
  538. .SH SEE ALSO
  539. ctags(1)
  540. .SH BUGS
  541. .I Cref
  542. occasionally flags a reference as a definition when it really isn't.  This
  543. most frequently happens after a
  544. .B struct.
  545.  
  546.